El paquete sympy
tiene un módulo de lógica. Con él podemos hacer algunas simplificaciones
In [1]:
from sympy import *
Definimos los símbolos que vamos a utilizar. Supremo e ínfimo se introducen usando el o e y lógicos. Aunque también podemos utilizar And
y Or
como funciones. Para la negación usamos Not
o ~
. Tenemos además Xor
, Nand
, Implies
(que se puede usar de forma prefija con >>
) y Equivalent
.
In [2]:
x, y, z = symbols("x,y,z")
In [3]:
p = (x | y) & ~ z
In [4]:
pprint(p)
La formas normales conjuntiva y disyuntivas las podemos calcular como sigue
In [5]:
to_cnf(p)
Out[5]:
In [6]:
to_dnf(p)
Out[6]:
También podemos simplificar expresiones
In [7]:
simplify(x | ~x)
Out[7]:
O dar valores de verdad a las variables
In [8]:
p.xreplace({x:True})
Out[8]:
Esto nos permite crear nuestras propias tablas de verdad
In [9]:
p.free_symbols
Out[9]:
In [10]:
p = Or(x,And(x,y))
In [11]:
from IPython.display import HTML,display
In [12]:
colores=['LightCoral','Aquamarine']
tabla="<table style='width:25%'><tr><td bgcolor='lightblue'>$"+latex(x)
tabla=tabla+"$ </td><td bgcolor='lightblue'>$"+latex( y)+"$</td><td bgcolor='lightblue'>$"+latex(p)+"$</td></tr>"
for t in cartes({True,False}, repeat=2):
v =dict(zip((x,y),t))
tabla=tabla+"<tr> <td bgcolor="+colores[v[x]]+">"+str(v[x])
tabla=tabla+"</td><td bgcolor="+colores[v[y]]+">"+str(v[y])
tabla=tabla+"</td><td bgcolor="+colores[v[x]]+">"+str(p.xreplace(v))+"</td></tr>"
tabla=tabla+"</table>"
display(HTML(tabla))
Una forma de comprobar que dos expresiones son equivalentes es la siguiente
In [13]:
Equivalent(simplify(p), simplify(x))
Out[13]:
Veamos ahora cómo podemos encontrar la versión simplificada de una función booleana que venga dada por minterms. Aparentemente SOPform
hace algunas simplificaciones usando el algoritmo de Quine-McCluskey
In [14]:
p=SOPform([x,y,z],[[0,0,1],[0,1,0],[0,1,1],[1,1,0],[1,0,0],[1,0,1]])
In [15]:
p
Out[15]:
Al utilizar sympy
podemos escribir una forma más amigable de una expresión booleana
In [16]:
pprint(p)
Los comandos simplify
or simplify_logic
pueden simplificar aún más
In [17]:
pprint(simplify(p))
In [18]:
pprint(simplify_logic(p))
De hecho, p
se puede escribir de forma más compacta. Para ello vamos a utilizar el algoritmo espresso, que viene implementado en el paquete pyeda
In [19]:
from pyeda.inter import *
Este paquete no admite las variables definidas con symbols
, así que las vamos a declarar con expvar
para definir variables booleanas
In [20]:
x,y,z = map(exprvar,"xyz")
In [21]:
p=SOPform([x,y,z],[[0,0,1],[0,1,0],[0,1,1],[1,1,0],[1,0,0],[1,0,1]])
Otro problema es que la salida de SOPform
no es una expresión de pyeda
. Lo podemos arreglar pasándola a cadena de caracteres y releyéndola en pyeda
In [22]:
p=expr(str(p))
Ahora sí que podemos utilizar el simplificador espresso implementado en pyeda
In [23]:
pm, =espresso_exprs(p)
In [24]:
pm
Out[24]:
Y podemos comprobar que es más corta que la salida que daba sympy
. Para escribirla de forma más "legible" volvemos a utilizar pprint
de sympy
, pero para ello necesitamos pasar nuestra expresión en pyeda
a sympy
In [25]:
pprint(sympify(pm))
Podríamos haber definido directamente p
utilizando tablas de verdad
In [26]:
p=truthtable([x,y,z], "01111110")
In [27]:
pm, = espresso_tts(p)
In [28]:
pprint(sympify(pm))
La tabla de verdad de una expresión se obtiene como sigue
In [29]:
expr2truthtable(pm)
Out[29]:
Veamos un ejemplo análogo pero con más variables, y de paso mostramos como definir vectores de variables
In [30]:
X = ttvars('x', 4)
f = truthtable(X, "0111111111111110")
In [31]:
fm, = espresso_tts(f)
In [32]:
fm
Out[32]:
In [33]:
expr2truthtable(fm)
Out[33]:
Veamos que el o exclusivo con la definición $x\oplus y=(x\wedge \neg y)\vee (\neg x\wedge y)$ es asociativo
In [34]:
x, y, z = map(exprvar,"xyz")
In [35]:
f = lambda x,y : Or(And(x,~ y),And(~x,y))
In [36]:
f(x,y)
Out[36]:
In [37]:
expr2truthtable(f(x,y))
Out[37]:
In [38]:
f(x,y).equivalent(Xor(x,y))
Out[38]:
Veamos que efectivamente $x\oplus(y\oplus z)=(x\oplus y)\oplus z$
In [39]:
pprint(simplify_logic(f(x,f(y,z))))
In [40]:
pprint(simplify_logic(f(f(x,y),z)))
In [41]:
a= f(f(x,y),z)
b= f(x,f(y,z))
In [42]:
a.equivalent(b)
Out[42]:
Podemos hacer una función que pase de minterm a expresions en pyeda
In [43]:
def minterm2expr(l,v):
n = len(l)
vv=v.copy()
for i in range(n):
if not(l[i]):
vv[i]=Not(vv[i])
return And(*vv)
In [44]:
x,y,z,t = map(exprvar,"xyzt")
In [45]:
minterm2expr([0,1,0,1],[x,y,z,t])
Out[45]:
In [46]:
def minterms2expr(l,v):
return Or(*[minterm2expr(a,v) for a in l])
In [47]:
hh2=minterms2expr([[0,0,0,0],[0,0,1,0],[0,1,0,0],[0,1,1,0],[0,1,1,1],[1,0,0,0],[1,0,1,0],[1,1,0,0]],[x,y,z,t])
In [48]:
hh2
Out[48]:
In [49]:
pprint(sympify(hh2))
Y ahora la podemos simplificar
In [50]:
sh2, = espresso_exprs(hh2)
In [51]:
sh2
Out[51]: